from __future__ import with_statement

import transaction
import time
from crv.model import DBSession, metadata
from crv.model.crvmodel import *

import logging
log = logging.getLogger(__name__)

from threading import Thread, Lock
from collections import deque

from crv.tools.singleton import Singleton
from crv.tools.statemachinemgr import StateMachineMgr

def checkState_INITIALIZED_Role(role):
    #STOPPED if vm is STOPPED 
    if role.vm.currentSC==VM.SC.SC_STOPPED:
        return True
    #Stopped if any of the role it depend on is stopped
    for idep in role.dependOn:
        if idep.getAttr("enabled")=="True" and idep.currentSC!=Role.SC.SC_STARTED:
            return True
    #stopped if vm is not stopping/stopped however exist other roles (non-stopepd) in the vm, and ReverseProfileApplied
    if role.getReverseProfileApplied():
        for irole in role.vm.role:
            if irole.Role_ID!=role.Role_ID and (irole.currentSC!=Role.SC.SC_STOPPED):
                #log.debug("checkState_INITIALIZED_Role::reverse profile applied, yet other roles %s in the vm is not stopped %s, return true"%(irole.getAttr("name"),Role.SCname[irole.currentSC]))
                return True

    return False
                    

def checkState_STARTINGVM_Role(role):
    for idep in role.dependOn:
        if idep.getAttr("enabled")=="True" and idep.currentSC!=Role.SC.SC_STARTED:
            return False
    
    #STARTINGVM if vm is STARTING
    if role.vm.currentSC==VM.SC.SC_STARTING:
        #log.debug("checkState_STARTINGVM_Role:: VMstarting, returning true")
        return True
    elif role.vm.currentSC==VM.SC.SC_STARTED:
        if role.getProfileProvided():
            #log.debug("checkState_STARTINGVM_Role:: VMstarted but profile provided, returning false")                
            return False
        elif role.getReverseProfileProvided():
            #log.debug("checkState_STARTINGVM_Role:: VMstarted and profile not provided but reverseprofile provided, should be in REMOVING, returning false")
            return False
        #log.debug("checkState_STARTINGVM_Role:: VMstarted and profile not provided and reverseprofile not provided,  returning true") 
        return True

    #log.debug("checkState_STARTINGVM_Role:: VM neither starting nor started, return False")
    return False

def checkState_APPLYING_Role(role):
    for idep in role.dependOn:
        if idep.getAttr("enabled")=="True" and idep.currentSC!=Role.SC.SC_STARTED:
            return False
    #VM must be started, and the profile must be provided in the server
    if role.vm.currentSC==VM.SC.SC_STARTED:
        if role.getProfileProvided():
            if role.getProfileApplied():
                #log.debug("checkState_APPLYING_Role:: VMstarted and profile provided and profile applied, returning false")                
                return False
            else:
                #log.debug("checkState_APPLYING_Role:: VMstarted and profile provided but profile not applied, returning true")
                return True
        else:    
            #log.debug("checkState_APPLYING_Role:: VMstarted but profile not provided, returning false")
            return False

    #log.debug("checkState_APPLYING_Role:: VM not started, return False")
    return False

def checkState_STARTED_Role(role):
    for idep in role.dependOn:
        if idep.getAttr("enabled")=="True" and idep.currentSC!=Role.SC.SC_STARTED:
            return False
    #VM must be running and the profile is applied
    if role.vm.currentSC==VM.SC.SC_STARTED and role.getProfileProvided() and role.getProfileApplied():
        #log.debug("checkState_STARTED_Role:: VMstarted and profile provided and profile applied, returning true")
        return True
    #log.debug("checkState_STARTED_Role:: returning false")
    return False


def checkState_REMOVING_Role(role):
    for idep in role.dependOn:
        if idep.getAttr("enabled")=="True" and idep.currentSC!=Role.SC.SC_STARTED:
            return False
    #VM must be started, and the profile must be provided in the server
    if role.vm.currentSC==VM.SC.SC_STARTED:
        if role.getReverseProfileProvided():
            if role.getReverseProfileApplied():
                for irole in role.vm.role:
                    if irole.Role_ID!=role.Role_ID and (irole.currentSC!=Role.SC.SC_STOPPED):
                        #log.debug("checkState_REMOVING_Role::reverse profile applied, yet other roles %s in the vm is not stopped %s, return false"%(irole.getAttr("name"),Role.SCname[irole.currentSC]))
                        return False
                #no other role in the same vm if reach here
                if role.vm.currentSC!=VM.SC.SC_STOPPING:
                    #log.debug("checkState_REMOVING_Role::vm started, reverse profile provided and applied, but VM.currentSC is not Stopping, return True")
                    return True
                else:
                    #log.debug("checkState_REMOVING_Role:: vm started and reverse profile provided and applied, also VM.currentSC is Stopping, should be in stopping vm, return False")
                
                    return False
            else:
                #log.debug("checkState_REMOVING_Role:: vm started and reserve profile provided but not applied,return true")
                return True
                
    #log.debug("checkState_REMOVING_Role:: return false")
    return False

def checkState_STOPPINGVM_Role(role):
    for idep in role.dependOn:
        if idep.getAttr("enabled")=="True" and idep.currentSC!=Role.SC.SC_STARTED:
            return False
    return role.vm.currentSC==VM.SC.SC_STOPPING

def checkState_EXCEPTION_Role(role):
    return False
    
def checkState_FIXING_Role(role):
    return False
    
    
#canAdvance is only needed for those state transitions that need to control its children, e.g. INITIALIZED->APPLYING, need to loop over roles and set targetSC=STARTED
#setting to exception states are automatic

def canAdvance_INITIALIZED_STARTINGVM_Role(role):
    #can startvm if all roles it depends on are running
    for idep in role.dependOn:
        if idep.currentSC!=Role.SC.SC_STARTED:
            #log.debug("canAdvance_INITIALIZED_STARTINGVM_Role::idep.currentSC=%s, returning false"%Role.SCname[idep.currentSC])
            return False
    #log.debug("canAdvance_INITIALIZED_STARTINGVM_Role:: returning True")
    return True

def doAdvance_INITIALIZED_STARTINGVM_Role(role):
    #log.debug("doAdvance_INITIALIZED_STARTINGVM_Role::setting vm.targetSC=VM.SC.SC_STARTED")

    role.setReverseProfileProvided(False)
    role.setReverseProfileApplied(False)
    role.setProfileProvided(False)
    role.setProfileApplied(False)
    
    role.vm.targetSC=VM.SC.SC_STARTED

def canAdvance_STARTINGVM_APPLYING_Role(role):
    return role.vm.currentSC==VM.SC.SC_STARTED

def doAdvance_STARTINGVM_APPLYING_Role(role):
    roleMgr=RoleMgr.getInstance()

    roleMgr.getProfileMaker(role).provideProfile(role)
    
    role.setProfileProvided(True)
    role.setProfileApplied(False)    
    role.setReverseProfileProvided(False)
    role.setReverseProfileApplied(False)
    

def canAdvance_STARTED_REMOVING_Role(role):
    #all roles that depends on it are STOPPED/STOPPINGVM
    for idepby in role.dependBy:
        if idepby.currentSC!=Role.SC.SC_STOPPED and idepby.currentSC!=Role.SC.SC_STOPPINGVM:
            #log.debug("canAdvance_STARTED_REMOVING_Role::idepby=%s in state %s, return false"%(idepby.getAttr("name"),Role.SCname[idepby.currentSC]))
            return False
    #log.debug("canAdvance_STARTED_REMOVING_Role::returning %s"%str(role.vm.currentSC==VM.SC.SC_STARTED))
    return role.vm.currentSC==VM.SC.SC_STARTED

def doAdvance_STARTED_REMOVING_Role(role):
    roleMgr=RoleMgr.getInstance()
    roleMgr.getProfileMaker(role).provideReverseProfile(role)
    role.setReverseProfileProvided(True)
    role.setReverseProfileApplied(False)
    role.setProfileProvided(False)
    role.setProfileApplied(False)
    pass

def canAdvance_REMOVING_STOPPINGVM_Role(role):
    return role.getReverseProfileApplied()

def doAdvance_REMOVING_STOPPINGVM_Role(role):
    for idep in role.vm.role:
        if idep.Role_ID!=role.Role_ID and idep.currentSC!=Role.SC.SC_REMOVING and idep.currentSC!=Role.SC.SC_STOPPED:
            #other role need this vm, do nothing
            return
    role.vm.targetSC=VM.SC.SC_STOPPED

class RoleMgr(Singleton):
    """
    @author Yushu Yao
    @version 0.1
    """

    def __init__(self):
        super(RoleMgr, self).__init__()
        self.__smgr = StateMachineMgr()
        
        self.__smgr.addNodes(Role.SCname.keys())
        self.__smgr.addEdge(Role.SC.SC_INITIALIZED, Role.SC.SC_STARTINGVM)
        self.__smgr.addEdge(Role.SC.SC_STARTINGVM, Role.SC.SC_APPLYING)        
        self.__smgr.addEdge(Role.SC.SC_STARTINGVM, Role.SC.SC_EXCEPTION)        
        self.__smgr.addEdge(Role.SC.SC_APPLYING, Role.SC.SC_STARTED)
        self.__smgr.addEdge(Role.SC.SC_APPLYING, Role.SC.SC_EXCEPTION)
        self.__smgr.addEdge(Role.SC.SC_STARTED, Role.SC.SC_REMOVING)
        self.__smgr.addEdge(Role.SC.SC_STARTED, Role.SC.SC_EXCEPTION)
        self.__smgr.addEdge(Role.SC.SC_REMOVING, Role.SC.SC_STOPPINGVM)
        self.__smgr.addEdge(Role.SC.SC_REMOVING, Role.SC.SC_EXCEPTION)
        self.__smgr.addEdge(Role.SC.SC_STOPPINGVM, Role.SC.SC_STOPPED)
        self.__smgr.addEdge(Role.SC.SC_STOPPINGVM, Role.SC.SC_EXCEPTION)        
        self.__smgr.addEdge(Role.SC.SC_EXCEPTION, Role.SC.SC_FIXING)
        self.__smgr.addEdge(Role.SC.SC_FIXING, Role.SC.SC_INITIALIZED)

        self.__smgr.setFunc_checkState(Role.SC.SC_INITIALIZED,checkState_INITIALIZED_Role)
        self.__smgr.setFunc_checkState(Role.SC.SC_STARTINGVM , checkState_STARTINGVM_Role )
        self.__smgr.setFunc_checkState(Role.SC.SC_APPLYING , checkState_APPLYING_Role )
        self.__smgr.setFunc_checkState(Role.SC.SC_STARTED , checkState_STARTED_Role )
        self.__smgr.setFunc_checkState(Role.SC.SC_REMOVING , checkState_REMOVING_Role )
        self.__smgr.setFunc_checkState(Role.SC.SC_STOPPINGVM , checkState_STOPPINGVM_Role )        
        self.__smgr.setFunc_checkState(Role.SC.SC_EXCEPTION , checkState_EXCEPTION_Role )
        self.__smgr.setFunc_checkState(Role.SC.SC_FIXING , checkState_FIXING_Role )

        self.__smgr.setFunc_canAdvance(Role.SC.SC_INITIALIZED, Role.SC.SC_STARTINGVM, canAdvance_INITIALIZED_STARTINGVM_Role)
        self.__smgr.setFunc_doAdvance (Role.SC.SC_INITIALIZED, Role.SC.SC_STARTINGVM, doAdvance_INITIALIZED_STARTINGVM_Role)
        self.__smgr.setFunc_canAdvance(Role.SC.SC_STARTINGVM,  Role.SC.SC_APPLYING,   canAdvance_STARTINGVM_APPLYING_Role)
        self.__smgr.setFunc_doAdvance (Role.SC.SC_STARTINGVM,  Role.SC.SC_APPLYING,   doAdvance_STARTINGVM_APPLYING_Role)
        self.__smgr.setFunc_canAdvance(Role.SC.SC_STARTED,     Role.SC.SC_REMOVING,   canAdvance_STARTED_REMOVING_Role)
        self.__smgr.setFunc_doAdvance (Role.SC.SC_STARTED,     Role.SC.SC_REMOVING,   doAdvance_STARTED_REMOVING_Role)
        self.__smgr.setFunc_canAdvance(Role.SC.SC_REMOVING,    Role.SC.SC_STOPPINGVM, canAdvance_REMOVING_STOPPINGVM_Role)
        self.__smgr.setFunc_doAdvance (Role.SC.SC_REMOVING,    Role.SC.SC_STOPPINGVM, doAdvance_REMOVING_STOPPINGVM_Role)

        self.__smgr.initialize(Role.SC, Role.SCname)

    def advanceStates(self,obj):
        return self.__smgr.advanceStates(obj)

    def getProfileMaker(self,role):
        return role.vm.rp.getMaker("profile")
        
